home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mntlib43 / mntlib / localtim.c < prev    next >
C/C++ Source or Header  |  1993-10-11  |  9KB  |  329 lines

  1. /* mktime, localtime, gmtime */
  2. /* written by ERS and placed in the public domain */
  3.  
  4. #include <stddef.h>
  5. #include <stdlib.h>
  6. #include <time.h>
  7. #include <ctype.h>
  8. #ifndef _COMPILER_H
  9. #include <compiler.h>
  10. #endif
  11.  
  12. struct tm *_gmtime __PROTO((const time_t *t, struct tm *stm));
  13.  
  14. #if 0
  15. static void
  16. DEBUG_TM(nm, tm)
  17.     char *nm;
  18.     struct tm *tm;
  19. {
  20.     char buf[100];
  21.  
  22.     (void)strftime(buf, 100, "%c %z", tm);
  23.     printf("%s: %s\n", nm, buf);
  24. }
  25. #else
  26. #define DEBUG_TM(nm, tm)
  27. #endif
  28.  
  29. #define SECS_PER_MIN    (60L)
  30. #define SECS_PER_HOUR   (3600L)
  31. #define SECS_PER_DAY    (86400L)
  32. #define SECS_PER_YEAR   (31536000L)
  33. #define SECS_PER_LEAPYEAR (SECS_PER_DAY + SECS_PER_YEAR)
  34.  
  35. time_t _timezone = -1;    /* holds # seconds west of GMT */
  36.  
  37. static int
  38. days_per_mth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  39.  
  40. static int
  41. mth_start[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
  42.  
  43. static time_t tzoffset __PROTO((char *s, int *hasdst));
  44. static int indst __PROTO((long secs, const struct tm *t));
  45.  
  46. static int dst = -1;    /* whether dst holds in current timezone */
  47.  
  48. /*
  49.  * FIXME: none of these routines is very efficient. Also, none of them
  50.  * handle dates before Jan 1, 1970.
  51.  *
  52.  */
  53.  
  54. /*
  55.  * mktime: take a time structure representing the local time (such as is
  56.  *  returned by localtime() and convert it into the standard representation
  57.  *  (as seconds since midnight Jan. 1 1970, GMT).
  58.  *
  59.  * Note that time() sends us such a structure with tm_yday and tm_wday
  60.  * undefined, so we shouldn't count on these being correct!
  61.  */
  62.  
  63. time_t
  64. mktime(t)
  65.         const struct tm *t;
  66. {
  67.         time_t s;
  68.         int y;
  69.  
  70. DEBUG_TM("mktime", t);
  71.         if (t->tm_year < 70)      /* year before 1970 */
  72.                 return (time_t) -1;
  73.  
  74. /* calculate tm_yday here */
  75.     y = (t->tm_mday - 1) + mth_start[t->tm_mon] + /* leap year correction */
  76.         ( ( (t->tm_year % 4) != 0 ) ? 0 : (t->tm_mon > 1) );
  77.  
  78.     s = (t->tm_sec)+(t->tm_min*SECS_PER_MIN)+(t->tm_hour*SECS_PER_HOUR) +
  79.         (y*SECS_PER_DAY)+((t->tm_year - 70)*SECS_PER_YEAR) +
  80.         ((t->tm_year - 69)/4)*SECS_PER_DAY;
  81.  
  82. /* Now adjust for the time zone and possible daylight savings time */
  83. /* note that we have to call tzset() every time; see 1003.1 sect 8.1.1 */
  84.     tzset();
  85.  
  86.         s += _timezone;
  87.         if (dst == 1 && indst(s, t))
  88.                 s -= SECS_PER_HOUR;
  89.  
  90.         return s;
  91. }
  92.  
  93.  
  94. struct tm *_gmtime(t, stm)
  95.         const time_t *t;
  96.     struct tm *stm;
  97. {
  98.         time_t  time = *t;
  99.         int     year, mday, i;
  100.  
  101.         if (time < 0)   /* negative times are bad */
  102.                 return 0;
  103.         stm->tm_wday = (int) (((time/SECS_PER_DAY) + 4) % 7);
  104.  
  105.         year = 70;
  106.         for (;;) {
  107.                 if (time < SECS_PER_YEAR) break;
  108.                 if ((year % 4) == 0) {
  109.                         if (time < SECS_PER_LEAPYEAR)
  110.                                 break;
  111.                         else
  112.                                 time -= SECS_PER_LEAPYEAR;
  113.                 }
  114.                 else
  115.                         time -= SECS_PER_YEAR;
  116.                 year++;
  117.         }
  118.         stm->tm_year = year;
  119.         mday = stm->tm_yday = (int)(time/SECS_PER_DAY);
  120.  
  121.         days_per_mth[1] = (year % 4) ? 28 : 29;
  122.         for (i = 0; mday >= days_per_mth[i]; i++)
  123.                 mday -= days_per_mth[i];
  124.         stm->tm_mon = i;
  125.         stm->tm_mday = mday + 1;
  126.  
  127.         time = time % SECS_PER_DAY;
  128.         stm->tm_hour = (int) (time/SECS_PER_HOUR);
  129.         time = time % SECS_PER_HOUR;
  130.         stm->tm_min = (int) (time/SECS_PER_MIN);
  131.         stm->tm_sec = (int) (time % SECS_PER_MIN);
  132.         stm->tm_isdst = 0;
  133.  
  134. DEBUG_TM("gmtime", stm);
  135.         return stm;
  136. }
  137.  
  138. struct tm *gmtime(t)
  139.     const time_t *t;
  140. {
  141.     static struct tm gtime;
  142.  
  143.     return _gmtime(t, >ime);
  144. }
  145.  
  146. /* given a standard time, convert it to a local time */
  147.  
  148. struct tm *localtime(t)
  149.         const time_t *t;
  150. {
  151.     static struct tm ltim;
  152.         struct tm *stm;
  153.         time_t gmsecs;  /*time in GMT */ 
  154.  
  155.     tzset();
  156.         gmsecs = *t - _timezone;
  157.         stm = _gmtime(&gmsecs, <im);
  158.     if (stm == NULL) return stm;        /* check for illegal time */
  159.         stm->tm_isdst = (dst == -1) ? -1 : 0;
  160.  
  161.         if (dst == 1 && indst(*t, (const struct tm *)stm)) {
  162.        /* daylight savings time in effect */
  163.                 stm->tm_isdst = 1;
  164.                 if (++stm->tm_hour > 23) {
  165.                         stm->tm_hour -= 24;
  166.                         stm->tm_wday = (stm->tm_wday + 1) % 7;
  167.                         stm->tm_yday++;
  168.                         stm->tm_mday++;
  169.                         if (stm->tm_mday > days_per_mth[stm->tm_mon]) {
  170.                                 stm->tm_mday = 1;
  171.                                 stm->tm_mon++;
  172.                         }
  173.                 }
  174.         }
  175.     DEBUG_TM("localtime", stm);
  176.         return stm;
  177. }
  178.  
  179. /*
  180.  * THIS IS A DELIBERATE VIOLATION OF THE ANSI STANDARD:
  181.  * there appears to be a conflict between Posix and ANSI; the former
  182.  * mandates a "tzset()" function that gets called whenever time()
  183.  * does, and which sets some global variables. ANSI wants none of
  184.  * this. Several Unix implementations have tzset(), and few people are
  185.  * going to be hurt by it, so it's included here.
  186.  */
  187.  
  188. /* set the timezone and dst flag to the local rules. Also sets the
  189.    global variable tzname to the names of the timezones
  190.  */
  191.  
  192. char *tzname[2] = {"UCT", "UCT"};
  193.  
  194. void
  195. tzset()
  196. {
  197.     _timezone = tzoffset(getenv("TZ"), &dst);
  198. }
  199.  
  200. /*
  201.  * determine the difference, in seconds, between the given time zone
  202.  * and Greenwich Mean. As a side effect, the integer pointed to
  203.  * by hasdst is set to 1 if the given time zone follows daylight
  204.  * savings time, 0 if there is no DST.
  205.  *
  206.  * Time zones are given as strings of the form
  207.  * "[TZNAME][h][:m][TZDSTNAME]" where h:m gives the hours:minutes
  208.  * east of GMT for the timezone (if [:m] does not appear, 0 is assumed).
  209.  * If the final field, TZDSTNAME, appears, then the time zone follows
  210.  * daylight savings time.
  211.  *
  212.  * Example: EST5EDT would represent the N. American Eastern time zone
  213.  *          CST6CDT would represent the N. American Central time zone
  214.  *          NFLD3:30NFLD would represent Newfoundland time (one and a
  215.  *              half hours ahead of Eastern).
  216.  *          OZCST-9:30 would represent the Australian central time zone.
  217.  *              (which, so I hear, doesn't have DST).
  218.  *
  219.  * NOTE: support for daylight savings time is currently very bogus.
  220.  * It's probably best to do without, unless you live in North America.
  221.  *
  222.  */
  223. #define TZNAMLEN    8    /* max. length of time zone name */
  224.  
  225. static
  226. time_t 
  227. tzoffset(s, hasdst)
  228.         char *s;
  229.         int  *hasdst;
  230. {
  231.         time_t off;
  232.         int x, sgn = 1;
  233.     static char stdname[TZNAMLEN+1], dstname[TZNAMLEN+1];
  234.     static char unknwn[4] = "???";
  235.  
  236.     char *n;
  237.  
  238.         *hasdst = -1;                   /* Assume unknown */
  239.         if (!s || !*s)
  240.                 return 0;               /* Assume GMT */
  241.  
  242.        *hasdst = 0;
  243.  
  244.     n = stdname;
  245.         while (*s && isalpha(*s)) {
  246.         *n++ = *s++;        /* skip name */
  247.     }
  248.     *n = 0;
  249.  
  250. /* now figure out the offset */
  251.  
  252.         x = 0;
  253.         if (*s == '-') {
  254.                 sgn = -1;
  255.                 s++;
  256.         }
  257.         while (isdigit(*s)) {
  258.                 x = 10 * x + toint(*s);
  259.                 s++;
  260.         }
  261.         off = x * SECS_PER_HOUR;
  262.         if (*s == ':') {
  263.                 x = 0;
  264.                 s++;
  265.                 while (isdigit(*s)) {
  266.                         x = 10 * x + toint(*s);
  267.                         s++;
  268.                 }
  269.             off += (x * SECS_PER_MIN);
  270.         }
  271.  
  272.     n = dstname;
  273.         if (isalpha(*s)) {
  274.                 *hasdst = 1;
  275.         while (*s && isalpha(*s)) *n++ = *s++;
  276.     }
  277.     *n = 0;
  278.  
  279.     if (stdname[0])
  280.         tzname[0] = stdname;
  281.     else
  282.         tzname[0] = unknwn;
  283.  
  284.     if (dstname[0])
  285.         tzname[1] = dstname;
  286.     else
  287.         tzname[1] = stdname;
  288.  
  289.         return sgn * off;
  290. }
  291.  
  292. /*
  293.  * Given a tm struct representing the local time, determine whether
  294.  * DST is currently in effect. This should only be
  295.  * called if it is known that the time zone indeed supports DST.
  296.  *
  297.  * FIXME: For now, assume that everyone follows the North American
  298.  *   time zone rules, all the time. This means daylight savings
  299.  *   time is assumed to be in effect from the first Sunday in April
  300.  *   to the last Sunday in October. Prior to 1987, the old rules
  301.  *   (last Sunday in April to last Sunday in Oct.) are used, even when
  302.  *   (as in 1974) they're not applicable. Sorry.
  303.  *
  304.  */
  305.  
  306. static
  307. int indst(s, t)
  308.     long s;
  309.         const struct tm *t;
  310. {
  311.         if (t->tm_mon == 3) {           /* April */
  312. /* before 1987, see if there's another sunday in the month */
  313.                 if (t->tm_year < 87 && t->tm_wday + 30 - t->tm_mday < 7)
  314.                         return 1;       /* no there isn't */
  315. /* after 1987, see if a sunday has happened yet */
  316.                 if (t->tm_wday - t->tm_mday < 0)
  317.                         return 1;       /* yep */
  318.                 return 0;
  319.         }
  320.         if (t->tm_mon == 9) {           /* October */
  321.                 if (t->tm_wday + 31 - t->tm_mday < 7)
  322.                         return 0;       /* there are no more sundays */
  323.                 return 1;
  324.         }
  325. /* Otherwise, see if it's a month between April and October exclusive */
  326.         return (t->tm_mon > 3 && t->tm_mon < 9);
  327. }
  328.  
  329.